using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Text;
using System.IO;
using System.Xml.Serialization;
using System.Xml;

namespace SAInterfaceLibrary
{
    public class ProjectBuildConfigSpec
    {
        private string _name;

        /// <summary>
        /// The name of this guy
        /// </summary>
        public string Name
        {
            get { return _name; }
            set { _name = value; }
        }

        /// <summary>
        /// Return a version of Name that is safe to use in the file system.
        /// </summary>
        public string SafeName
        {
            get
            {
                return _name;
            }
        }

        private string _config_arguments;

        /// <summary>
        /// Get or set the arguments that are passed to the config script.
        /// </summary>
        public string ConfigArguments
        {
            get { return _config_arguments; }
            set
            {
                _config_arguments = value;
                _config_arguments = _config_arguments.Replace("\n", "").Replace("\r", "");
            }
        }

        private Dictionary<string, EnvironmentVariable> _env_vars = new Dictionary<string, EnvironmentVariable>();

        /// <summary>
        /// Returns a list of all the environment variables. There must be a better way to code the
        /// "get"!
        /// </summary>
        public List<EnvironmentVariable> EnvironmentVariables
        {
            get
            {
                List<EnvironmentVariable> result = new List<EnvironmentVariable>();
                foreach (EnvironmentVariable e in _env_vars.Values)
                {
                    result.Add(e);
                }
                return result ;
            }
            set
            {
                _env_vars.Clear();
                foreach (EnvironmentVariable e in value)
                {
                    _env_vars[e.Name] = e;
                }
            }

        }

        /// <summary>
        /// Add a new env var to the list of the ones that we will have defined.
        /// </summary>
        /// <param name="name"></param>
        /// <param name="value"></param>
        public void AddEnvVar(string name, string value)
        {
            _env_vars[name] = new EnvironmentVariable(name, value);
        }

        private List<string> _make_args;

        /// <summary>
        /// List of make arguments that should be on the make command line.
        /// </summary>
        public List<string> MakeArguments
        {
            get { return _make_args; }
            set { _make_args = value; }
        }


        /// <summary>
        /// Load a default config.
        /// </summary>
        /// <returns></returns>
        public static ProjectBuildConfigSpec LoadDefaultConfig()
        {
            return LoadConfig(Configuraiton.Instance.DefaultConfigName);
        }

        /// <summary>
        /// Find a particular config using the standard search path: first the
        /// user's directory and then the system directory.
        /// </summary>
        /// <param name="p"></param>
        /// <returns></returns>
        public static ProjectBuildConfigSpec LoadConfig(string p)
        {
            ProjectBuildConfigSpec result = LoadConfigFromDir(p, Configuraiton.Instance.UserConfigDirectory);
            if (result != null)
            {
                return result;
            }

            return LoadConfigFromDir(p, Configuraiton.Instance.SystemConfigDirectory);
        }

        /// <summary>
        /// Look at a directory and see if we can find what we need!
        /// </summary>
        /// <param name="p"></param>
        /// <param name="p_2"></param>
        /// <returns></returns>
        private static ProjectBuildConfigSpec LoadConfigFromDir(string config_name, string dir_name)
        {
            Dictionary<string, FileInfo> file_table_keys = LoadConfigDirInfo(dir_name);
            if (file_table_keys.ContainsKey(config_name))
            {
                return LoadConfigFromFile(file_table_keys[config_name]);
            }
            return null;
        }

        /// <summary>
        /// Loads a config xml file.
        /// </summary>
        /// <param name="p"></param>
        /// <returns></returns>
        public static ProjectBuildConfigSpec LoadConfigFromFile(string p)
        {
            FileInfo fi = new FileInfo(p);
            return LoadConfigFromFile(fi);
        }

        /// <summary>
        /// Do the actual loading of a file.
        /// </summary>
        /// <param name="fi"></param>
        /// <returns></returns>
        public static ProjectBuildConfigSpec LoadConfigFromFile(FileInfo fi)
        {
            if (!fi.Exists)
            {
                return null;
            }

            XmlSerializer xreader = new XmlSerializer(typeof(ProjectBuildConfigSpec));
            using (StreamReader st = fi.OpenText())
            {
                ProjectBuildConfigSpec spec = (ProjectBuildConfigSpec)xreader.Deserialize(st);
                return spec;
            }
        }

        /// <summary>
        /// Loads the names of all configs that are out there in that directory. Not the
        /// cheapest way to do it, but there are so few this should be quick (I hope).
        /// </summary>
        /// <param name="dir_name"></param>
        /// <returns></returns>
        private static Dictionary<string, FileInfo> LoadConfigDirInfo(string dir_name)
        {
            Dictionary<string, FileInfo> result = new Dictionary<string, FileInfo>();
            DirectoryInfo di = new DirectoryInfo(dir_name);
            if (!di.Exists)
            {
                return result;
            }

            foreach (FileInfo fi in di.GetFiles())
            {
                if (fi.Extension == ".config_spec")
                {
                    ProjectBuildConfigSpec spec = LoadConfigFromFile(fi);
                    if (!result.ContainsKey(spec.Name))
                    {
                        result[spec.Name] = fi;
                    }
                }
            }
            return result;
        }

        /// <summary>
        /// Try to do the save of the release.
        /// </summary>
        public void Save()
        {
            string path = Configuraiton.Instance.UserConfigDirectory + @"\" + Name + ".config_spec";
            SaveConfigToFile(path);
        }

        /// <summary>
        /// Save the config to a specific file.
        /// </summary>
        /// <param name="path"></param>
        public void SaveConfigToFile(string path)
        {
            XmlSerializer saver = new XmlSerializer(typeof(ProjectBuildConfigSpec));
            FileInfo fi = new FileInfo(path);

            if (!fi.Directory.Exists)
            {
                fi.Directory.Create();
            }

            using (XmlTextWriter wr = new XmlTextWriter(fi.CreateText()))
            {
                saver.Serialize(wr, this);
            }
        }

        /// <summary>
        /// Load up all the known configuration names! Whoo!
        /// </summary>
        /// <returns></returns>
        public static IEnumerable<string> AllKnownConfigNames()
        {
            Dictionary<string, int> what_we_did = new Dictionary<string, int>();
            foreach (string relname in KnownConfigsInDir(Configuraiton.Instance.UserConfigDirectory))
            {
                if (!what_we_did.ContainsKey(relname))
                {
                    yield return relname;
                    what_we_did[relname] = 1;
                }
            }
            foreach (string relname in KnownConfigsInDir(Configuraiton.Instance.SystemConfigDirectory))
            {
                if (!what_we_did.ContainsKey(relname))
                {
                    yield return relname;
                    what_we_did[relname] = 1;
                }
            }
        }

        /// <summary>
        /// Ok -- get a list of all configs in a directory.
        /// </summary>
        /// <param name="p"></param>
        /// <returns></returns>
        private static IEnumerable<string> KnownConfigsInDir(string p)
        {
            Dictionary<string, FileInfo> files = LoadConfigDirInfo(p);
            foreach (string relname in files.Keys)
            {
                yield return relname;
            }
        }
    }
}
